25 实践课-意图识别模型优化与验证
意图识别模型优化与验证
关联:索引
术语小抄(初学者版)
-
基线模型(Baseline Model):上一讲已经训练出的第一版可运行模型,用作本次优化的起点。
-
错例(Error Case):模型预测错误的样本,是定位问题和制定优化方案的直接证据。
-
分类边界(Decision Boundary):两个意图之间的区分标准;边界写不清,模型就容易混淆。
-
标注误差(Label Noise):样本标签错了、字段不一致、同一句话多人标成不同意图等问题。
-
数据扩充(Data Augmentation):补充更多高质量样本,让模型看到更多表达方式和边界情况。
-
留出测试集验证(Test Set Validation):用训练时没有参与学习的数据评估模型,判断模型是否真正学会而不是“记住了训练集”。
-
场景指令测试(Scenario Validation):用贴近真实产线或项目现场的口语化指令测试模型可用性。
-
建议环境:Python 3.10/3.11,与前序口径一致。
-
配套项目目录:沿用 24 讲你们已创建/已拿到的“意图识别训练项目目录”(中以
.\01_intent_training_project\作为示例名称;若你们实际目录名不同,以实际为准)。 -
配套项目应包含(以 24 讲为准):
check_intent_data.py、train_intent_model.py、predict_intent.py、requirements.txt、model.json、intents.json -
意图集合统一口径:以配套项目的
intents.json为准,并与model.json中的label_map一致(不要在不同目录各自维护一套 intent 名称)。 -
建议优先在配套项目目录中安装依赖,而不是在当前目录临时拼装环境。可执行:
cd ".\01_intent_training_project"
py -m pip install -U pip
py -m pip install -r .\requirements.txt
解释与自检:
-
cd ".\01_intent_training_project":先进入与 24 讲对应的真实项目目录,后续训练、预测、模型文件保存都在这里发生。 -
该项目核心依赖仍然是
sentence-transformers与numpy;requirements.txt的好处是让机房和学生机尽量保持一致。 -
如果机房网络较慢或权限受限,优先按 01 的环境排障顺序处理,不要临时切换陌生环境,否则会影响“前后版本可比性”。
-
每次优化必须保留
dataset_version、model_version与“本次改动说明”,否则无法比较前后效果。 -
每次验证至少输出:
accuracy、macro_f1、confusion_matrix或等价错例统计。
1)什么叫“优化成功”
本讲不把“单一指标变高”直接等同于优化成功,而是同时看 3 件事:
- 验证集或留出测试集指标是否提升,至少要看
accuracy与macro_f1。 - 关键混淆类别是否得到改善,而不是“强势类别更强、弱势类别更弱”。
- 实际场景指令测试是否更稳,尤其是模糊表达、边界样本、口语化表达是否更可控。
2)意图识别准确率低的常见原因
- 数据量不足:每个意图样本太少,表达方式单一,模型只学会了少量固定句式。
- 分类边界模糊:例如
sort_by_grade与query_apple_quality、device_control与query_sorting_status的定义不清,导致同一句话可被多种方式理解。 - 标注误差:样本标签错误、同类样本用不同意图命名、
slots写法不统一、旧版本规范与新版本混用。 - 参数与阈值不合理:学习率过大、轮数过多/过少、类别不平衡处理不足、
unknown/clarify阈值设置不合理,导致误判率高。
3)优化优先级(避免一上来乱调参数)
- 先检查数据质量,再调模型参数。
- 先补“最容易混淆的类别对”数据,再盲目补总量。
- 先修分类规则与标注规范,再重训。
- 只有在数据与规则基本稳定后,才做学习率、轮数、阈值等参数微调。
4)双重验证口径
本讲要求每组至少做两类验证:
- 验证集/测试集验证:证明模型在标准样本上是否提升;当前配套项目默认直接给出验证集结果,如需独立测试集需额外准备留出数据。
- 实际指令测试:证明模型在现场口语表达、模糊表达、边界表达下是否更稳定。
AI 工具使用:诊断原因 / 生成优化方案 / 生成扩充脚本 / 辅助答疑(学生可直接复制)
使用方法:把你们的“意图集合、错例、混淆矩阵、数据样本、训练日志、当前指标”粘贴到
{你的内容}。要求 AI 输出结构化结论,便于人工审计与落地执行。
模板目录:
- 模板 1:生成模型问题诊断清单
- 模板 2:生成个性化优化方案
- 模板 3:生成数据扩充脚本或扩充样本
- 模板 4:排查优化过程中的技术问题
模板 1:生成模型问题诊断清单
你是意图识别模型诊断助手。下面是我的模型测试结果(含 accuracy、macro-F1、混淆矩阵、错例列表、意图定义)。
请输出:
1)<<<CAUSES>>>:按优先级排序的低准确率原因,必须区分“数据量不足 / 分类边界模糊 / 标注误差 / 参数问题”
2)<<<EVIDENCE>>>:每个原因对应的证据(从错例、混淆类别、样本分布中提取)
3)<<<ACTIONS>>>:每个原因的最小修复动作(先做什么,后做什么)
4)<<<CHECKLIST>>>:我下一轮优化前的自检清单(至少 10 条)
输出要求:
- 必须严格包含 CAUSES/EVIDENCE/ACTIONS/CHECKLIST 标记
- 不要只给结论,必须指出证据来自哪里
我的数据与日志:{你的内容}
模板 2:生成个性化优化方案
你是课堂项目助教。请根据我的意图识别模型问题,生成一份“个性化优化方案”,要求:
1)分成“数据优化 / 规则优化 / 参数优化 / 验证方案”四部分;
2)每部分给 3~5 条可执行动作;
3)参数优化只能基于课堂已使用的方法,不能引入新框架;
4)最后输出“本轮优化优先级排序”和“预计收益最大的 2 个动作”。
我的项目情况:{你的内容}
模板 3:生成数据扩充脚本或扩充样本
你是 Python 实践课助教。请为我的意图识别项目生成“数据扩充脚本”或“扩充样本草案”,要求:
1)围绕指定意图类别生成同义表达、口语表达、边界表达;
2)输出必须保持课堂 JSONL 字段口径:sample_id/raw_text/intent/slots/scene/guideline_version/ts_ms;
3)不能直接复制已有样本,必须做表达变化;
4)如生成脚本,只能用 Python 标准库;
5)最后附带“人工审计清单”,提醒我检查标签是否正确、字段是否一致、是否存在重复样本。
我的意图集合与示例数据:{你的内容}
模板 4:优化过程报错排查与答疑
下面是我在意图识别模型优化过程中的报错、日志片段或异常现象:{你的内容}
请按“定位→修复→预防”输出:
1)先判断属于哪类问题:数据格式 / 标签集合不一致 / 训练参数 / 预测阈值 / 验证方法 / 其他
2)给最多 8 步排查路径,每步写清楚要检查什么、预期结果是什么
3)给一份回归验证清单(至少 8 条),确保修复后没有引入新的问题
- 为什么同样训练过的模型,有的组准确率比较高,有的组却总在几个意图之间来回混淆?
- 如果你只看到
accuracy=0.82,能不能直接判断模型“够用了”?为什么还要看错例和场景测试?
- 学会用错例、样本分布、混淆矩阵和场景指令来定位低准确率原因。
- 为你们组当前模型制定一份“有优先级、有证据、有动作”的优化方案。
上一讲我们已经能输出 accuracy、macro_f1 与 confusion_matrix。本讲的重点不是“再跑一次指标”,而是回答下面 3 个问题:
- 哪些类别最容易被混淆?
- 这些混淆是因为样本少、标签乱、边界不清,还是参数不合理?
- 本轮优化先改哪里,最可能带来真实提升?
- 先看整体指标:确认问题规模有多大。
- 再看按类别指标:找出“掉链子”的少数类或边界类。
- 最后看错例:确认错误发生在什么样的语言表达和什么样的场景上下文里。
1)数据量不足
典型表现:
- 某些意图类别只有几条样本,或样本都长得很像。
- 模型对训练集常见句式效果还行,但一换说法就错。
判断依据:
- 意图分布严重不均衡。
- 错例主要出现在“没见过的新表达”上。
2)分类边界模糊
典型表现:
query_sorting_status与device_control、sort_by_grade与query_apple_quality等意图互相混淆。- 同一句话在不同小组方案中会被标成不同标签。
判断依据:
- 混淆矩阵里总是同一对类别互相误判。
- 标注规范写不清“不属于它的情况”。
3)标注误差
典型表现:
- 数据里混有拼写漂移、旧标签、空字段、重复样本。
- 同样意思的句子标签不一致,或
slots格式前后不统一。
判断依据:
- 运行数据自检脚本会报错。
- 错例里出现“模型其实没错,是标签本身错了”的情况。
4)参数或阈值问题
典型表现:
- 训练指标波动大,重复运行结果不稳定。
- 明显不确定的指令也被强行预测为某个执行类意图。
判断依据:
- 学习率过大、轮数过多导致波动或过拟合。
threshold太低,导致本应输出unknown/clarify的样本被错误执行。
说明:
- 这段脚本不负责训练模型,而是帮助你把已有验证结果转成“可读的诊断信息”。
- 输入假设为
eval_report.json,其中包含:整体指标、按类别统计、混淆类别对、错例列表。
示例输入格式(最小可用版):
{
"accuracy": 0.81,
"macro_f1": 0.72,
"per_label": {
"sort_by_grade": {"support": 18, "recall": 0.61},
"query_apple_quality": {"support": 16, "recall": 0.56},
"device_control": {"support": 20, "recall": 0.90}
},
"top_confusions": [
{"true": "query_apple_quality", "pred": "sort_by_grade", "count": 5},
{"true": "sort_by_grade", "pred": "query_apple_quality", "count": 4}
],
"errors": [
{"text": "最近10秒A级有多少", "true": "query_apple_quality", "pred": "sort_by_grade"},
{"text": "把A级苹果送到1号口", "true": "sort_by_grade", "pred": "query_apple_quality"}
]
}
解释与自检:
per_label:按类别统计支持数和召回率,用来找“弱类别”。top_confusions:记录最严重的互相混淆类别对,用来判断是否存在边界问题。errors:保留真实错例,方便后续人工复盘和 AI 分析。
把下面脚本保存为 diagnose_intent_model.py:
from __future__ import annotations
# diagnose_intent_model.py(课堂诊断脚本)
# 目标:把“评估报告 eval_report.json”转成可读结论,帮助你快速定位:
# - 哪些意图是弱类别(recall 低)
# - 哪些意图对互相混淆最严重(top_confusions)
# - 可能原因是什么(样本不足 / 边界不清 / 标签质量问题)
#
# 输入:eval_report.json(来自你们训练/评估流程的输出,或按示例手工整理)
# 输出:打印 4 段内容:
# - accuracy/macro_f1(整体指标概览)
# - weak_labels(弱类别列表)
# - likely_causes(推测原因列表)
# - sample_errors(错例样本,便于人工复盘或交给 AI 分析)
import json
import sys
from pathlib import Path
from typing import Dict, List
def load_report(path: Path) -> Dict:
# 读取评估报告 JSON:
# - 这里假设文件是 UTF-8 编码
# - 返回值类型用 Dict 简化,课堂更关注“能跑通 + 可解释”
return json.loads(path.read_text(encoding="utf-8"))
def weak_labels(per_label: Dict[str, Dict], *, recall_threshold: float) -> List[Dict]:
# 从 per_label 中筛选“召回率低于阈值”的弱类别
# per_label 结构示例:
# {
# "sort_by_grade": {"support": 18, "recall": 0.61},
# "query_apple_quality": {"support": 16, "recall": 0.56}
# }
items: List[Dict] = []
for label, stats in per_label.items():
# support:该类别样本数(支持数),少数类通常更难学
support = int(stats.get("support", 0))
# recall:召回率,反映“该类样本被模型找回的比例”
recall = float(stats.get("recall", 0.0))
if recall < recall_threshold:
items.append({"label": label, "support": support, "recall": recall})
# 排序策略:先看 recall(越低越优先),再看 support(越少越可能是数据不足)
items.sort(key=lambda x: (x["recall"], x["support"]))
return items
def guess_causes(report: Dict) -> List[str]:
# 根据“弱类别 + 高混淆对”给出第一轮推测原因(可直接用于制定优化优先级)
reasons: List[str] = []
# 做健壮性处理:避免 report 字段缺失导致脚本崩溃(课堂机房常见)
per_label = report.get("per_label", {}) if isinstance(report.get("per_label"), dict) else {}
confusions = report.get("top_confusions", []) if isinstance(report.get("top_confusions"), list) else []
# 弱类别原因推测:
# - support 太少:优先补该类样本(尤其是口语/边界表达)
# - support 不少但 recall 低:优先排查边界定义与标签质量
for item in weak_labels(per_label, recall_threshold=0.70):
if item["support"] < 12:
reasons.append(f"{item['label']}: 样本可能不足(support={item['support']},recall={item['recall']:.2f})")
else:
reasons.append(f"{item['label']}: 虽然样本不算少,但 recall 偏低,需检查边界或标签质量")
# 混淆对原因推测:
# - true -> pred 的 count 越高,说明该对类别边界可能不清或错例表达高度重叠
for item in confusions[:5]:
true_label = str(item.get("true", ""))
pred_label = str(item.get("pred", ""))
count = int(item.get("count", 0))
if true_label and pred_label and count > 0:
reasons.append(f"{true_label} -> {pred_label}: 高混淆(count={count}),优先检查分类边界与错例表达")
return reasons
def main() -> None:
# 命令行入口:
# python diagnose_intent_model.py <eval_report.json>
if len(sys.argv) < 2:
print("usage: python diagnose_intent_model.py <eval_report.json>")
sys.exit(2)
report_path = Path(sys.argv[1])
report = load_report(report_path)
# 取整体指标(若字段不存在则回退为 0.0,便于脚本继续跑)
acc = float(report.get("accuracy", 0.0))
macro_f1 = float(report.get("macro_f1", 0.0))
# per_label:按类别统计;errors:错例列表(用于抽样打印)
per_label = report.get("per_label", {}) if isinstance(report.get("per_label"), dict) else {}
errors = report.get("errors", []) if isinstance(report.get("errors"), list) else []
# 1)整体指标
print(json.dumps({"accuracy": acc, "macro_f1": macro_f1}, ensure_ascii=False))
# 2)弱类别列表
print("=== weak_labels ===")
for item in weak_labels(per_label, recall_threshold=0.70):
print(json.dumps(item, ensure_ascii=False))
# 3)推测原因(用于制定下一步优化动作)
print("=== likely_causes ===")
for reason in guess_causes(report):
print(f"- {reason}")
# 4)错例样本(只打印前 10 条避免刷屏;课堂上可以把这 10 条交给 AI 做归因与扩写)
print("=== sample_errors ===")
for item in errors[:10]:
print(json.dumps(item, ensure_ascii=False))
if __name__ == "__main__":
main()
逐段解释与自检要点:
load_report():读取评估报告 JSON,作为诊断输入;脚本本身不依赖第三方库,便于机房直接运行。guess_causes(...):把“低召回 + 高混淆”转成自然语言提示,帮助你先做第一轮问题分类。
运行示例(PowerShell):
py .\diagnose_intent_model.py .\eval_report.json
解释与自检:
- 这条命令的目的不是“重新训练”,而是把已有结果转成诊断结论。
- 如果输出里出现某个类别
support很低,优先考虑补数据,而不是立刻调学习率。 - 如果输出里反复出现同一对类别互相混淆,优先检查分类边界与标注规范是否清晰。
六、练习(至少完成 2 题)
1)用你们组当前模型的测试结果,整理一份 eval_report.json,至少包含:整体指标、按类别统计、前 5 个混淆类别对、前 10 条错例。
- 提示:不要只抄最终指标,真正有价值的是“哪些类别错、为什么错”。
2)根据诊断结果,为你们组写出一份“本轮优化优先级表”,至少列出 3 项问题与对应动作。
- 提示:动作要具体到“补哪类样本、改哪个边界、调哪个参数、怎么验证”,不能只写“继续优化”。
- 如果你已经知道问题来自“类别混淆”,接下来应该先补数据、先改规则,还是先调参数?为什么?
- 如果测试集指标提升了,但现场口语指令仍然容易误判,这算不算真正优化成功?
- 完成一轮基于标注数据的模型优化,包括数据扩充、分类规则调整、参数微调。
- 用验证集对比或留出测试集验证,再结合场景指令测试证明本轮优化是否真正有效。
1)扩充标注数据
适用场景:
-
样本总量偏少。
-
某类意图表达方式太单一。
-
口语化、边界型、容易歧义的说法覆盖不足。
-
先补高价值样本,再补大量样本。
-
优先补“最容易混淆的类别对”与“现场常见表达”。
2)调整分类规则与标注规范
适用场景:
-
同一句话可能被标成不同意图。
-
某些意图的定义写得过宽或过窄。
-
每个意图都要写清“定义、边界、反例、澄清条件”。
-
规范一旦更新,必须同步升级
guideline_version。
3)微调训练参数与推理阈值
适用场景:
-
数据与边界已基本稳定,但指标仍有提升空间。
-
模型对不确定输入“过于自信”或“过于保守”。
-
学习率
lr -
训练轮数
epochs -
权重衰减
weight_decay -
拒识阈值
threshold -
本讲不涉及对完整大语言模型做端到端微调训练。
-
这里调整的是当前项目中的训练超参数与推理阈值配置,例如
lr、epochs、weight_decay,以及推理阶段对p_max的解释方式。
说明:
- 这段脚本用于把“人工样本 + AI 生成样本”合并成新的训练集。
- AI 生成样本必须先人工看一遍,再交给脚本合并;脚本负责兜底,不代替人工审计。
把下面脚本保存为 merge_augmented_data.py:
from __future__ import annotations
# merge_augmented_data.py(合并扩充数据 + 最小清洗脚本)
# 目标:把“人工标注 base.jsonl”与“AI 生成 aug.jsonl(已人工审计)”合并成新训练集 output.jsonl。
# 只做最小清洗(兜底),不替代人工审计:
# - 必填字段检查(字段齐全且类型基本正确)
# - 过滤空文本
# - intent 必须在 intents.json 中
# - slots 必须是 dict(保持结构一致,便于后续扩展)
# - 去重:按 sample_id 去重;同时按 (raw_text, intent) 去重防止“换 id 重复写入”
#
# 用法:
# python merge_augmented_data.py <base.jsonl> <aug.jsonl> <intents.json> <output.jsonl>
import json
import sys
from pathlib import Path
from typing import Dict, List, Set
REQUIRED_FIELDS = {
"sample_id",
"raw_text",
"intent",
"slots",
"scene",
"guideline_version",
"ts_ms",
}
def load_jsonl(path: Path) -> List[Dict]:
# 读取 JSONL(JSON Lines):每行一个 JSON 对象
# 课堂上更推荐 JSONL 而不是单个巨大 JSON 数组,因为它更易追加与排错
rows: List[Dict] = []
for i, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
s = line.strip()
if not s:
continue
try:
rows.append(json.loads(s))
except json.JSONDecodeError as exc:
# 明确指出第几行坏了,便于学生快速定位格式问题
raise RuntimeError(f"json decode error at line {i}: {exc}") from exc
return rows
def load_intents(path: Path) -> Set[str]:
# 读取合法 intent 集合(intents.json 必须是 JSON 数组)
# 目的:防止 AI 生成不存在的标签,导致训练时“标签漂移”
data = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(data, list):
raise RuntimeError("intents.json must be a JSON array")
return {str(x) for x in data}
def validate_row(row: Dict, *, intents: Set[str]) -> bool:
# 行级校验:不通过就返回 False(在 merge_rows 里会被过滤掉)
# 注意:这里是“最小校验”,不做复杂业务校验(比如 slots 的字段范围),课堂后续可扩展
if REQUIRED_FIELDS - set(row.keys()):
return False
if not isinstance(row.get("raw_text"), str) or not row["raw_text"].strip():
return False
if row.get("intent") not in intents:
return False
if not isinstance(row.get("slots"), dict):
return False
return True
def merge_rows(base_rows: List[Dict], aug_rows: List[Dict], *, intents: Set[str]) -> List[Dict]:
# 合并与去重:
# - seen_ids:防止 sample_id 重复
# - seen_pairs:防止同一句 raw_text 在同一 intent 下重复出现(AI 扩写时常见)
seen_ids: Set[str] = set()
seen_pairs: Set[tuple[str, str]] = set()
merged: List[Dict] = []
for row in base_rows + aug_rows:
# 先做基本校验,避免后续处理 KeyError/类型错误
if not validate_row(row, intents=intents):
continue
sample_id = str(row["sample_id"]).strip()
raw_text = str(row["raw_text"]).strip()
intent = str(row["intent"]).strip()
dedup_key = (raw_text, intent)
# 去重 1:同 sample_id 的样本只保留第一次出现的
if sample_id in seen_ids:
continue
# 去重 2:同一句 raw_text + intent 只保留一次(减少训练集的“重复表达污染”)
if dedup_key in seen_pairs:
continue
seen_ids.add(sample_id)
seen_pairs.add(dedup_key)
merged.append(row)
return merged
def save_jsonl(path: Path, rows: List[Dict]) -> None:
# 保存为 JSONL:每行一条 JSON,方便 diff、追加与排障
text = "\n".join(json.dumps(r, ensure_ascii=False) for r in rows)
path.write_text(text, encoding="utf-8")
def main() -> None:
# 命令行入口:
# python merge_augmented_data.py <base.jsonl> <aug.jsonl> <intents.json> <output.jsonl>
if len(sys.argv) < 5:
print("usage: python merge_augmented_data.py <base.jsonl> <aug.jsonl> <intents.json> <output.jsonl>")
sys.exit(2)
# 参数解析:按位置读取(课堂更直观;真实工程可换 argparse)
base_path = Path(sys.argv[1])
aug_path = Path(sys.argv[2])
intents_path = Path(sys.argv[3])
out_path = Path(sys.argv[4])
# 1)加载合法意图集合
intents = load_intents(intents_path)
# 2)加载 base 与 aug 两份数据
base_rows = load_jsonl(base_path)
aug_rows = load_jsonl(aug_path)
# 3)合并 + 清洗 + 去重
merged = merge_rows(base_rows, aug_rows, intents=intents)
# 4)写出新数据集版本(建议命名 intent_data_v2.jsonl / v3 ...)
save_jsonl(out_path, merged)
# 输出统计信息:课堂用于快速确认“合并是否生效、是否过滤过多”
print(json.dumps({"base": len(base_rows), "aug": len(aug_rows), "merged": len(merged)}, ensure_ascii=False))
if __name__ == "__main__":
main()
逐段解释与自检要点:
validate_row(...):检查字段是否齐全、文本是否为空、标签是否合法、slots是否仍为字典。merge_rows(...):同时按sample_id和(raw_text, intent)去重,避免“同一句话换个 ID”反复写入。save_jsonl(...):把合并后的结果重新保存为标准 JSONL,便于后续训练脚本直接读取。
运行示例(PowerShell):
py .\merge_augmented_data.py .\intent_data.jsonl .\intent_data_ai_aug.jsonl .\intents.json .\intent_data_v2.jsonl
解释与自检:
- 第 1 个参数
intent_data.jsonl:原始训练集。 - 第 2 个参数
intent_data_ai_aug.jsonl:AI 生成并经过人工审计的扩充样本。 - 第 3 个参数
intents.json:合法意图集合,防止 AI 随手生成不存在的标签。 - 第 4 个参数
intent_data_v2.jsonl:新一轮训练集输出文件。 - 如果
merged数量明显小于base + aug,通常说明去重或字段校验过滤掉了一部分样本,这是正常现象;关键是搞清楚过滤原因。 - 合并完成后,不要直接重训,建议立刻复用配套项目里的
check_intent_data.py再做一次正式自检,确认没有重复 id、非法 intent、空文本等问题。
说明:
- 这里直接复用
.\01_intent_training_project\中已经实现好的check_intent_data.py、train_intent_model.py与predict_intent.py,不重复讲解完整训练代码。 - 当前配套项目默认会把训练产物写回
model.json,因此在重训前必须先备份旧模型,否则“优化前后对比”会丢失基线。 - 第一层:读取训练脚本直接输出的
final_val_acc / final_val_macro_f1 / confusion_matrix,作为验证集证据。 - 重点在于“同一套口径、两版数据/参数、两轮结果对比”,而不是重新搭框架。
1)进入项目目录并备份优化前模型:
cd ".\01_intent_training_project"
Copy-Item .\model.json .\model_v0.4_baseline.json
解释与自检:
cd ".\01_intent_training_project":后续命令都在项目目录里运行,避免找不到脚本或把model.json写到错误位置。Copy-Item .\model.json .\model_v0.4_baseline.json:把优化前模型显式备份出来,防止训练脚本覆盖掉原有model.json。- 如果你们当前基线版本不是
v0.4,可以按自己组的实际版本改文件名,但必须让“基线模型”和“优化后模型”同时保留下来。
2)把新数据写入项目目录并先做正式自检:
py .\check_intent_data.py .\intent_data_v2.jsonl .\intents.json
解释与自检:
- 当前配套项目自带的是
check_intent_data.py,不是evaluate_intent_model.py。 - 这一步的目的,是在重训前先确认合并后的数据集没有明显结构问题;若输出
issues_count非 0,应先修数据再训练。 - 自检通过后,再进入训练与验证环节,才能保证后续指标有解释力。
3)用新数据重新训练模型:
py .\train_intent_model.py .\intent_data_v2.jsonl .\intents.json v0.2 v0.5
解释与自检:
intent_data_v2.jsonl:表示已经完成一轮扩充或清洗后的数据版本。v0.2:数据版本号;你需要在优化记录里说明“和 v0.1 相比改了什么”。v0.5:模型版本号;每次重训都要递增,避免不同模型文件混淆。- 训练完成后,项目会覆盖写出新的
model.json,并在终端输出final_val_acc、final_val_macro_f1与confusion_matrix(rows=true, cols=pred)。 - 这组输出就是当前项目现成可用的“验证集证据”,应截图或复制到优化记录中。
4)读取训练输出,完成“优化前后”验证集对比:
- 优化前:整理上一次训练保留下来的
final_val_acc、final_val_macro_f1、confusion_matrix - 优化后:整理本次终端输出的同口径结果
- 对比重点:整体指标是否提升、主要混淆类别对是否减少、少数类是否改善
解释与自检:
- 当前项目没有独立的批量评估脚本,因此不要把不存在的
evaluate_intent_model.py当作默认前提。 - 如果教师后续另外提供了
test_set.jsonl与批量评估脚本,可以作为“留出测试集验证”的增强环节;但本项目的基础实现本身只保证“训练时的验证集输出 + 单条推理脚本”。
5)用实际场景指令做现场验证:
py .\predict_intent.py .\model.json "把A级苹果送到1号口"
py .\predict_intent.py .\model.json "最近10秒A级苹果有多少"
py .\predict_intent.py .\model.json "先别动,看看分拣线现在状态"
解释与自检:
- 第一句重点验证
sort_by_grade。 - 第二句重点验证
query_apple_quality。 - 第三句重点验证查询类与控制类的边界是否更清晰。
- 如果你们项目版本的输出字段与本不一致,以实际脚本输出为准,但仍建议至少记录:预测意图、最高置信度(或等价分数)、是否触发拒识/澄清策略(若有)。
- 场景验证不要只测“最标准的样本”,还要测口语化、模糊化、边界化表达,否则很难判断模型现场是否真正可用。
1)验证集对比表
| 模型版本 | 数据版本 | accuracy | macro-F1 | 主要混淆对 | 结论 |
|---|---|---|---|---|---|
| v0.4 | v0.1 | 0.81 | 0.72 | query_apple_quality ↔ sort_by_grade |
基线模型(来自上一次训练输出) |
| v0.5 | v0.2 | 0.87 | 0.81 | 同类混淆减少 | 本轮优化有效(来自本次训练输出) |
2)场景指令验证表
| 测试指令 | 期望意图 | 优化前 | 优化后 | 是否改进 |
|---|---|---|---|---|
| 把A级苹果送到1号口 | sort_by_grade |
query_apple_quality |
sort_by_grade |
是 |
| 最近10秒A级苹果有多少 | query_apple_quality |
sort_by_grade |
query_apple_quality |
是 |
| 先别动,看看分拣线现在状态 | query_sorting_status |
device_control |
query_sorting_status |
是 |
- 如果后续教师另行提供留出测试集评估脚本,再把“测试集验证”作为增强环节加入,不要在基础项目未提供时强行写成默认命令。
- 场景指令测试关注“真实表达是否变稳”。
- 两者都提升,才更接近真实可用的优化。
七、练习(至少完成 2 题)
1)围绕你们组最严重的 2 个混淆类别,各补充至少 10 条高质量样本,并说明这些样本为什么有价值。
- 提示:优先补边界样本、口语样本、澄清样本,而不是机械重复同一句话。
2)比较优化前后模型在同一测试集上的 accuracy、macro_f1 与主要混淆类别对变化,写出 100 字左右结论。
- 提示:不要只写“提升了”,而要写“哪类问题改善了、还有什么没解决”。
课程思政(融入点:通过模型迭代培养攻坚克难的探索精神)
- 模型优化不是一次“调参碰运气”,而是“发现问题 → 查证原因 → 设计方案 → 验证结果”的严谨过程。这个过程训练的不是机械重复,而是面对复杂问题时持续分析、持续迭代、持续改进的能力。
- 在产业场景里,真正有价值的技术不是“能演示一次”,而是“能稳定落地、能持续优化、能经得住复验”。通过本次意图识别模型优化,学生应体会到数据驱动和证据驱动对产业应用落地的真实价值。
- 你们本轮优化里最难解决的问题是什么?你们是如何一步步定位出来的?
- 如果模型误判导致现场动作错误,你们准备如何通过“数据版本、模型版本、验证记录”去追溯并修复?
作业:布置
1)提交模型优化报告(含问题分析、优化方案、前后准确率对比,300 字左右)。
2)提交优化后的模型代码、测试集验证结果截图、实际指令测试记录。
3)提交 AI 交互记录(辅助诊断问题、生成优化方案的过程)。
参考与延伸
- Sentence-Transformers:https://www.sbert.net/
- NumPy:https://numpy.org/
- JSON Lines:https://jsonlines.org/